/** \file log.h
 * \verbatim
 * General C/C++ logging/debug functions and macros
 * 
 * Copyright (c) 2001-2003 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#ifndef __LOG_H_INCLUDED
#define __LOG_H_INCLUDED

#include <scos.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

#ifdef __cplusplus
#if defined (__GNUC__) && (__GNUC__ < 3)
#include <lsstream>
#else
#include <sstream>
#endif /* __GNUC__ */
#endif /* __cplusplus */

/// Version of the logging system.
#define LOG_VERSION "4.1.1"

/// Maximum number of threads (hash table size).
#define LOG_MAX_THREADS 16384

/// Maximum number of microseconds to wait for lock.
#define LOG_MAX_LOCK_TIME 1000000

#define LOG_DONT_FLUSH_STREAMS

#define LOG_LEAVEPROGRAM() raise(SIGINT)

/// Defines stream types.
enum {LOG_STREAM_FD, LOG_STREAM_FILE, LOG_STREAM_SOCKET};

/// Defines stream and group operations.
enum {LOG_STREAM_DISABLE, LOG_STREAM_ENABLE,
      LOG_STREAM_SET_NOFLUSH, LOG_STREAM_SET_FLUSH,
      LOG_GROUP_DISABLE, LOG_GROUP_ENABLE, LOG_GROUP_SET_LEVEL,
      LOG_GROUP_SET_FLUSH, LOG_GROUP_SET_NOFLUSH,
            LOG_STREAM_GET_ENABLED,LOG_STREAM_GET_FLUSH};

/// Defines a registered thread.
typedef struct log_thread_s
{
  int used;       ///< Whether this entry is used.

  int id;         ///< Thread ID.
  char *name;     ///< Thread name.
} log_thread_t;

/// Defines a group.
typedef struct log_group_s
{
  char *name;     ///< Name of the group for reference purposes.
} log_group_t;

/// Defines a stream.
typedef struct log_stream_s
{
  int enabled;                ///< Whether writing to this stream is enabled.
  int group;                  ///< Group that this stream belongs to.

  char *header;               ///< Header format string.
  char *file;                 ///< Filename to log to.
  int type;                   ///< Type of stream.
  int flush;                  ///< Whether to flush this stream.

  FILE *fd;                   ///< File to log to.
  SOCKET socket;              ///< Socket to log to.
  struct sockaddr_in address; ///< Internet address to log to.
} log_stream_t;

/// Defines a set of default streams.
typedef struct log_default_streams_s
{
  int crit;                   ///< Logging stream for critical errors.
  int err;                    ///< Logging stream for normal errors.
  int warning;                ///< Logging stream for warnings.
  int notice;                 ///< Logging stream for notifications.
  int info;                   ///< Logging stream for general information.
  int debug;                  ///< Logging stream for debug information.
  int crawl;                  ///< Logging stream for step-by-step debugging.
} log_default_streams_t;

/// Defines the order of the default streams.
enum log_default_streams_e {LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE,
                            LOG_INFO, LOG_DEBUG, LOG_CRAWL,
                            LOG_NUM_DEFAULT_STREAMS};

/** \def __LOG_FUNCTIONS_SUPPORTED__
 * \brief Defined if the compiler can log function names.
 */
/** \def __MACRO_VARARGS_SUPPORTED__
 * \brief Defined if the preprocessor supports macros with a variable number
 *        of arguments.
 */
/** \def LOG_FUNC
 * \brief Name of the current function.
 *
 * Defined to be the appropriate C-compiler definition.
 * \li \c __FUNCTION__ for GCC 2.8.1 and higher.
 * \li \c __func__ for other C99 compilers.
 * \li \c NULL if not supported.
 */
#define LOG_FUNC __func__

/// Encapsulation of the initial parameters to lprintf.
/** \param stream logging stream
 * \see lprintf()
 */
#define STREAM(stream) __FILE__, __LINE__, LOG_FUNC, stream

/// Exit -1 if \p v returns non-zero.
/** \param s encapsulated logging stream.
  * \param v usually a function call.
  */
#define abortOnError(s, v)         {if ((v))\
        {lprintf(s, "Function dependency failed: " #v); LOG_LEAVEPROGRAM();}}

/// Return -1 if \p v returns non-zero.
/** \see abortError */
#define returnOnError(s, v)        {if ((v))\
        {lprintf(s, "Function dependency failed: " #v); return(-1);}}

/// Exit -1 if \p v returns a negative value.
/** \param s encapsulated logging stream.
  * \param v usually a function call.
  */
#define abortOnNegative(s, v)         {if ((v)<0)\
        {lprintf(s, "Function dependency failed: " #v); LOG_LEAVEPROGRAM();}}

/// Return -1 if \p v returns a negative value.
/** \see abortError */
#define returnOnNegative(s, v)        {if ((v)<0)\
        {lprintf(s, "Function dependency failed: " #v); return(-1);}}

/// Exit -1 if \p v is less than \p l or greater than \p u.
/**
 * \param s encapsulated logging stream.
 * \param v usually a variable.
 * \param l lower bound.
 * \param u upper bound.
 */
#define abortOnBounds(s, v, l, u)  {if (((v) < (l)) || ((v) > (u)))\
        {lprintf(s, "Bounds check failed: " #l " < " #v " > " #u);\
         LOG_LEAVEPROGRAM();}}

/// Return -1 if \p v is less than \p l or greater than \p u.
/** \see abortOnBounds */
#define returnOnBounds(s, v, l, u) {if (((v) < (l)) || ((v) > (u)))\
        {lprintf(s, "Bounds check failed: " #l " < " #v " > " #u);\
         return(-1);}}

/// Exit -1 if \p v returns NULL.
/** \param s encapsulated logging stream.
  * \param v usually a call to malloc(). */
#define abortOnNULL(s, v)          {if (!(v))\
        {lprintf(s, "NULL check failed: " #v); LOG_LEAVEPROGRAM();}}

/// Return -1 if \p v returns NULL.
/** \see abortOnNULL */
#define returnOnNULL(s, v)         {if (!(v))\
        {lprintf(s, "NULL check failed: " #v); return(-1);}}

/// Print an error message and exit -1.
/**
 * \param x arguments to lprintf.
 * \par Example:
 * \code
 * abortErrorl((STREAM(log.crit),
 *             "CRC Error in file '%s'; %d <> %d", f, crc1, crc2));
 * \endcode
*/
#define abortErrorl(x)           {\
        {lprintf x; LOG_LEAVEPROGRAM();}}

/// Print an error message and return -1.
/** \see abortErrorl */
#define returnErrorl(x)          {\
        {lprintf x; return(-1);}}

#ifdef __MACRO_VARARGS_SUPPORTED__
/// Print an error message and exit -1.
/**
 * \note
 * Only available on preprocessors that support macros with a variable number
 * of arguments.
 * \param s encapsulated logging stream
 * \param ... format and variables.
 * \par Example:
 * \code
 * abortError(STREAM(log.error),
 *            "CRC Error in file '%s'; %d <> %d", f, crc1, crc2);
 * \endcode
 */
  #define abortError(s, ...)         {\
          {lprintf(s, __VA_ARGS__); LOG_LEAVEPROGRAM();}}

/// Print an error message and return -1.
/** \see abortError */
  #define returnError(s, ...)        {\
          {lprintf(s, __VA_ARGS__); return(-1);}}
#endif /* __MACRO_VARARGS_SUPPORTED__ */

#ifdef __cplusplus
/// Macro for the logging of C++ streams
/** \param stream encapsulated logging stream.
  * \param message C++ stream formatted message.
  */
#define lprints(stream, message)\
        {std::ostringstream __oss; __oss << message; lprintf(stream, __oss.str().c_str());}
#endif /* __cpluslus */

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#undef LIBSYMBOL
#ifdef __LIBLOG
#define LIBSYMBOL LIBEXPORTSYMBOL
#else
#define LIBSYMBOL LIBIMPORTSYMBOL
#endif /* __LIBLOG */

#ifndef LIBDYNAMIC
void liblog_init(void);
void liblog_fini(void);
#endif

#if defined(__LOG_C) || !defined(LOG_DISABLE)

/// Add a group.
LIBSYMBOL int logAddGroup(void);

/// Add a named group.
LIBSYMBOL int logAddNamedGroup(const char *name);

/// Get the identifier for the first stream in a group.
LIBSYMBOL int logGetFirstGroupStream(const char *name);

/// Get the identifier of the next stream within a group.
LIBSYMBOL int logGetNextGroupStream(int stream);

/// Add a stream to the current group.
LIBSYMBOL int logAddStream(const char* header, const char* file);

/// Add default streams to the current group.
LIBSYMBOL log_default_streams_t logAddDefaultStreams(const char *file);

/// Get default streams for the program-default group.
LIBSYMBOL log_default_streams_t logGetDefaultStreams(void);

/// Edit specific streams, or all the streams in a stream's group.
LIBSYMBOL int logEdit(int stream, int operation);

LIBSYMBOL void logPrintHeader(const char* file, int line,
                    const char* function,
                    int s, char *buf);

/// Output buffer to a stream.
LIBSYMBOL void logOut(int s, char *buf);

/// Print a log message.
LIBSYMBOL void lprintf(const char* file, int line, const char* function,
             int stream, const char* msg, ...);

/// Register a thread.
LIBSYMBOL void logRegisterThread(const char *name);

/// Unregister a thread.
LIBSYMBOL void logUnRegisterThread(void);

#else

#define logAddGroup()                 0
#define logAddNamedGroup(name)        0
#define logGetFirstGroupStream(name)  0
#define logGetNextGroupStream(stream) 0
#define logAddStream(header, file)    0
#define logAddDefaultStreams(file)    ((log_default_streams_t){0, 0, 0, 0, 0, 0, 0})
#define logGetDefaultStreams()        ((log_default_streams_t){0, 0, 0, 0, 0, 0, 0})
#define logEdit(stream, operation)    0
#define logPrintHeader(file, line, function, s, buf)
#define logOut(s, buf)
#define lprintf(s, ...)
#define logRegisterThread(name)
#define logUnRegisterThread()

#endif

#undef LIBSYMBOL

#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */

#endif /* __LOG_H_INCLUDED */
